AWS CDKでCloudFrontのLambda Function URLsへのOACを設定してみた
AWS CDKでもLambda Function URLsのOACを設定したい
こんにちは、のんピ(@non____97)です。
皆さんはAWS CDKでもLambda Function URLsへのOACを設定したいなと思ったことはありますか? 私はあります。
先日、CloudFrontがLambda Function URLsへのOACをサポートしました。これによりLambda Function URLsへのアクセス制限がやりやすくなりました。詳細は以下記事をご覧ください。
Lambda Function URLsをマネジメントコンソールから設定する際はCloudFront側でOACの設定をした後、表示されたAWS CLIのスクリプトを実行する必要があります。地味に手間です。
ふと、CloudFormationでOACのプロパティを眺めていると、Lambda Function URLsをもうサポートしていました。早いですね。
OriginAccessControlOriginType
The type of origin that this origin access control is for.
Required: Yes
Type: String
Pattern: ^(s3|mediastore|lambda|mediapackagev2)$
Update requires: No interruption
AWS::CloudFront::OriginAccessControl OriginAccessControlConfig - AWS CloudFormation
CloudFormationで設定できるということはAWS CDKでも設定できるということです。
実際にやってみます。
やってみた
AWS CDKのコードの紹介
AWS CDKのコードは以下リポジトリに保存しています。
OACの設定をするにあたって、やっていることは以下の2つです。
- L1 ConstructでOACを作成する
- CloudFront distributionのL2 Constructに対してEscape hatchesで、OACを指定する
- Lambda関数にCloudFront distributionからの
lambda:InvokeFunctionUrl
を許可する
該当のコードは以下です。
// Lambda Function
const wasshoiLambda = new cdk.aws_lambda_nodejs.NodejsFunction(
this,
"WasshoiLambda",
{
entry: path.join(__dirname, "../src/lambda/wasshoi/index.ts"),
runtime: cdk.aws_lambda.Runtime.NODEJS_20_X,
bundling: {
minify: true,
tsconfig: path.join(__dirname, "../src/lambda/tsconfig.json"),
format: cdk.aws_lambda_nodejs.OutputFormat.ESM,
},
architecture: cdk.aws_lambda.Architecture.ARM_64,
loggingFormat: cdk.aws_lambda.LoggingFormat.JSON,
}
);
// CloudFront Distribution
this.distribution = new cdk.aws_cloudfront.Distribution(this, "Default", {
defaultBehavior: {
origin: new cdk.aws_cloudfront_origins.FunctionUrlOrigin(
wasshoiLambda.addFunctionUrl({
authType: cdk.aws_lambda.FunctionUrlAuthType.AWS_IAM,
})
),
allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD,
cachedMethods: cdk.aws_cloudfront.CachedMethods.CACHE_GET_HEAD,
cachePolicy: new cdk.aws_cloudfront.CachePolicy(this, "WasshoiCache", {
minTtl: cdk.Duration.seconds(1),
maxTtl: cdk.Duration.seconds(31536000),
defaultTtl: cdk.Duration.seconds(86400),
enableAcceptEncodingBrotli: true,
enableAcceptEncodingGzip: true,
queryStringBehavior:
cdk.aws_cloudfront.CacheQueryStringBehavior.allowList("wasshoi"),
}),
viewerProtocolPolicy:
cdk.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
responseHeadersPolicy:
cdk.aws_cloudfront.ResponseHeadersPolicy.SECURITY_HEADERS,
},
httpVersion: cdk.aws_cloudfront.HttpVersion.HTTP2_AND_3,
priceClass: cdk.aws_cloudfront.PriceClass.PRICE_CLASS_200,
domainNames: props.domainName ? [props.domainName] : undefined,
certificate: props.domainName
? props.certificateConstruct?.certificate
: undefined,
logBucket: props.cloudFrontAccessLogBucketConstruct?.bucket,
logFilePrefix: props.logFilePrefix,
});
// OAC
const cfnOriginAccessControl =
new cdk.aws_cloudfront.CfnOriginAccessControl(
this,
"OriginAccessControl",
{
originAccessControlConfig: {
name: "Origin Access Control for Lambda Functions URL",
originAccessControlOriginType: "lambda",
signingBehavior: "always",
signingProtocol: "sigv4",
},
}
);
const cfnDistribution = this.distribution.node
.defaultChild as cdk.aws_cloudfront.CfnDistribution;
// Set OAC
cfnDistribution.addPropertyOverride(
"DistributionConfig.Origins.0.OriginAccessControlId",
cfnOriginAccessControl.attrId
);
// Add permission Lambda Function URLs
wasshoiLambda.addPermission("AllowCloudFrontServicePrincipal", {
principal: new cdk.aws_iam.ServicePrincipal("cloudfront.amazonaws.com"),
action: "lambda:InvokeFunctionUrl",
sourceArn: `arn:aws:cloudfront::${
cdk.Stack.of(this).account
}:distribution/${this.distribution.distributionId}`,
});
S3バケットのOAC設定をする場合は、問答無用で設定されるOAIを剥がすための記述がいくつか必要でしたが、Lambda Function URLsの場合はシンプルです。
Lambda関数で実行する処理はwasshoi
というクエリで指定した値の数分だけわっしょい!!
を出力するものです。
import { Callback, LambdaFunctionURLEvent, Context } from "aws-lambda";
export const handler = async (
event: LambdaFunctionURLEvent,
context: Context,
callback: Callback
) => {
const wasshoi = Math.round(Number(event.queryStringParameters?.wasshoi || 0));
const message =
!Number.isInteger(wasshoi) || wasshoi <= 0
? "わっしょい! したくないのですか ... ?"
: wasshoi >= 10
? "お静かに"
: Array(wasshoi).fill("わっしょい!!").join(" ");
return {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: JSON.stringify({
message,
}),
};
};
デプロイ
実際にデプロイして試してみます。
設定は以下のようにしています。カスタムドメインで試してみたかったのでCloudFrontにlambda-url.non-97.net
という名前でアクセスできるようにしています。
export const lambdaOacStackProperty: LambdaOacStackProperty = {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
props: {
hostedZone: {
zoneName: "lambda-url.non-97.net",
},
certificate: {
certificateDomainName: "lambda-url.non-97.net",
},
contentsDelivery: {
domainName: "lambda-url.non-97.net",
},
allowDeleteBucketAndObjects: true,
cloudFrontAccessLog: {
enableAccessLog: true,
lifecycleRules: [{ expirationDays: 365 }],
},
logAnalytics: {
createWorkGroup: true,
enableLogAnalytics: ["cloudFrontAccessLog"],
},
},
};
大元のAWS CDKのコードは以下記事で紹介したものです。
動作確認
動作確認をします。
$ curl "https://lambda-url.non-97.net/?wasshoi=1"
{"message":"わっしょい!!"}
$ curl "https://lambda-url.non-97.net/?wasshoi=2"
{"message":"わっしょい!! わっしょい!!"}
$ curl "https://lambda-url.non-97.net/"
{"message":"わっしょい! したくないのですか ... ?"}
$ curl "https://lambda-url.non-97.net/?wasshoi=-1"
{"message":"わっしょい! したくないのですか ... ?"}
$ curl "https://lambda-url.non-97.net/?wasshoi=a"
{"message":"わっしょい! したくないのですか ... ?"}
$ curl "https://lambda-url.non-97.net/?wasshoi=8"
{"message":"わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!!"}
$ curl "https://lambda-url.non-97.net/?wasshoi=10"
{"message":"お静かに"}
$ curl "https://lambda-url.non-97.net/?wasshoi=5.1"
{"message":"わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!!"}
$ curl "https://lambda-url.non-97.net/?wasshoi=5.5"
{"message":"わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!!"}
wasshoi
で指定した値の数分だけ、わっしょいしていますね。
CloudFormationとAWS CDKでも設定できます
AWS CDKでCloudFrontとLambda Function URLsのOACを設定してみました。
API Gatewayがtoo muchである場合や、API Gatewayの統合リクエストタイムアウト29秒の制約が気になる場合に役立ちそうですね。
CloudFrontがLambda Function URLsへのOACをサポートしたことによるユースケースはwatany(@_watany)さんの以下記事も非常に参考になります。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!